<?xml version="1.0" encoding="UTF-8"?>
<?altova_samplexml Northwind.xml?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dml="http://www.nourysolutions.com/salamanca/DML" exclude-result-prefixes="dml">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <!-- Creates a name attribute -->
  <xsl:template name="name-attribute">
    <xsl:param name="element" />
    <xsl:param name="prefix" />
    <xsl:param name="suffix" />
    <xsl:param name="name" select="'name'" />
    <xsl:attribute name="{$name}">
      <xsl:choose>
        <xsl:when test="$element/@shortName">
          <xsl:value-of select="concat($prefix, $element/@shortName, $suffix)" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="concat($prefix, $element/@name, $suffix)" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
  </xsl:template>

  <!-- Creates a foreign name attribute -->
  <xsl:template name="foreign-name-attribute">
    <xsl:param name="element" />
    <xsl:param name="name" select="'name'" />
    <xsl:attribute name="{$name}">
      <xsl:choose>
        <xsl:when test="$element/@shortName">
          <xsl:value-of select="$element/@shortName" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$element/@name" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
  </xsl:template>

  <!-- Creates the column attributes related to a database type description -->
  <xsl:template name="sql-type-attributes">
    <xsl:param name="type" />
    <xsl:param name="size" />
    <xsl:variable name="sv">
      <xsl:choose>
        <xsl:when test="/dml:dml/dml:serializable[@name=$type/@name]/dml:sql/@size and (not($size) or (string-length($size)=0) or (/dml:dml/dml:serializable[@name=$type/@name]/dml:sql/@size &lt; $size))">
          <xsl:value-of select="/dml/serializable[@name=$type/@name]/sql/@size" />
        </xsl:when>
        <xsl:when test="/dml:dml/dml:type[@name=$type/@name]/dml:base/@size and (not($size) or (string-length($size)=0) or (/dml:dml/dml:type[@name=$type/@name]/dml:base/@size &lt; $size))">
          <xsl:value-of select="/dml:dml/dml:type[@name=$type/@name]/dml:base/@size" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$size" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:choose>
      <xsl:when test="/dml:dml/dml:serializable[@name=$type/@name]">
        <xsl:attribute name="type">
          <xsl:value-of select="/dml:dml/dml:serializable[@name=$type/@name]/dml:sql/@dbtype" />
        </xsl:attribute>
        <xsl:if test="$sv and (string-length($sv) &gt; 0)">
          <xsl:attribute name="size">
            <xsl:value-of select="$sv" />
          </xsl:attribute>
        </xsl:if>
      </xsl:when>
      <xsl:when test="/dml:dml/dml:type[@name=$type/@name]">
        <xsl:call-template name="sql-type-attributes">
          <xsl:with-param name="type" select="/dml:dml/dml:type[@name=$type/@name]/dml:base" />
          <xsl:with-param name="size" select="$sv" />
        </xsl:call-template>
      </xsl:when>
    </xsl:choose>
  </xsl:template>
  
  <!-- Apply the templates on the specified element with the specified mode and name -->
  <xsl:template name="foreign-item-apply">
    <xsl:param name="element" />
    <xsl:param name="mode" />
    <xsl:param name="name" />
    <xsl:variable name="n">
      <xsl:if test="string-length($name) &gt; 0">
        <xsl:value-of select="$name" />
      </xsl:if>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="$mode='foreign-column'">
        <xsl:apply-templates select="." mode="foreign-column">
          <xsl:with-param name="name" select="$n" />
        </xsl:apply-templates>
      </xsl:when>
      <xsl:when test="$mode='foreign-key'">
        <xsl:apply-templates select="." mode="foreign-key">
          <xsl:with-param name="name" select="$n" />
        </xsl:apply-templates>
      </xsl:when>
    </xsl:choose>
  </xsl:template>
  
  <!-- Creates a foreign key related description -->
  <xsl:template name="foreign-item">
    <xsl:param name="name" />
    <xsl:param name="mode" />
    <xsl:for-each select="/dml:dml/dml:assoc/dml:end[@target=$name]">
      <xsl:variable name="ua" select="count(../dml:end[@multiplicity='0..1' or @multiplicity='1'])" />
      <xsl:choose>
        <xsl:when test="$ua=2">
          <xsl:if test="../dml:end[@target=$name and position()=2]">
            <xsl:call-template name="foreign-item-apply">
              <xsl:with-param name="element" select="." />
              <xsl:with-param name="mode" select="$mode" />
            </xsl:call-template>
          </xsl:if>
        </xsl:when>
        <xsl:when test="$ua=1">
          <xsl:if test="@multiplicity!='1' and @multiplicity!='0..1'">
            <xsl:call-template name="foreign-item-apply">
              <xsl:with-param name="element" select="." />
              <xsl:with-param name="mode" select="$mode" />
            </xsl:call-template>
          </xsl:if>
        </xsl:when>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>
  
  <!-- Creates a column for a foreign key for the selected association end -->
  <xsl:template match="dml:end" mode="foreign-column">
    <xsl:variable name="n" select="@name" />
    <xsl:variable name="targetName" select="../dml:end[@name!=$n]/@target" />
    <xsl:param name="name" />
    <xsl:param name="foreignTable" select="false()" />
    <xsl:variable name="pname">
      <xsl:choose>
        <xsl:when test="string-length($name) &gt; 0">
          <xsl:value-of select="$name" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$targetName" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:element name="column">
      <xsl:for-each select="/dml:dml/dml:domain[@name=$targetName]/dml:attribute[../dml:key/@attributes=@name]">
        <xsl:call-template name="name-attribute">
          <xsl:with-param name="element" select="." />
          <xsl:with-param name="prefix" select="concat($pname, '_')" />
        </xsl:call-template>
        <xsl:call-template name="sql-type-attributes">
          <xsl:with-param name="type" select="dml:type" />
          <xsl:with-param name="size" select="dml:type/@size" />
        </xsl:call-template>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
  
  <!-- Creates a foreign key description for the selected association end -->
  <xsl:template match="dml:end" mode="foreign-key">
    <xsl:variable name="n" select="@name" />
    <xsl:variable name="targetName" select="../dml:end[@name!=$n]/@target" />
    <xsl:param name="name" />
    <xsl:param name="foreignTable" select="false()" />
    <xsl:variable name="pname">
      <xsl:choose>
        <xsl:when test="string-length($name) &gt; 0">
          <xsl:value-of select="$name" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$targetName" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:element name="foreign-key">
      <xsl:call-template name="name-attribute">
        <xsl:with-param name="element" select="/dml:dml/dml:domain[@name=$targetName]" />
        <xsl:with-param name="name" select="'foreignTable'" />
      </xsl:call-template>
      <xsl:call-template name="name-attribute">
        <xsl:with-param name="element" select="/dml:dml/dml:domain[@name=$targetName]" />
        <xsl:with-param name="prefix" select="concat(@target, '_FK_')" />
      </xsl:call-template>
      <xsl:for-each select="/dml:dml/dml:domain[@name=$targetName]/dml:attribute[../dml:key/@attributes=@name]">
        <xsl:element name="reference">
          <xsl:call-template name="name-attribute">
            <xsl:with-param name="element" select="." />
            <xsl:with-param name="prefix" select="concat($pname, '_')" />
            <xsl:with-param name="name" select="'local'" />
          </xsl:call-template>
          <xsl:call-template name="name-attribute">
            <xsl:with-param name="element" select="." />
            <xsl:with-param name="name" select="'foreign'" />
          </xsl:call-template>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
  
  <!-- Creates a table for the selected association description -->
  <xsl:template match="dml:assoc" mode="foreign-table">
    <xsl:element name="table">
      <!-- Name of the table -->
      <xsl:call-template name="name-attribute">
        <xsl:with-param name="element" select="." />
      </xsl:call-template>
      <!-- Columns -->
      <xsl:apply-templates select="dml:end" mode="foreign-column">
        <xsl:with-param name="foreignTable" select="true()" />
      </xsl:apply-templates>
      <!-- Foreign keys -->
      <xsl:apply-templates select="dml:end" mode="foreign-key">
        <xsl:with-param name="foreignTable" select="true()" />
      </xsl:apply-templates>
    </xsl:element>
  </xsl:template>
  
  <!-- Creates a table for the selected enumeration description -->
  <xsl:template match="dml:enum" mode="foreign-table">
    <xsl:element name="table">
      <!-- Name of the table -->
      <xsl:call-template name="name-attribute">
        <xsl:with-param name="element" select="." />
      </xsl:call-template>
      <!-- Columns -->
      <xsl:element name="column">
        <xsl:attribute name="name">Id</xsl:attribute>
        <xsl:call-template name="sql-type-attributes">
          <xsl:with-param name="type" select="dml:base" />
          <xsl:with-param name="size" select="dml:base/@size" />
        </xsl:call-template>
      </xsl:element>
    </xsl:element>
  </xsl:template>

  <!-- Creates a column description for the selected attribute description -->
  <xsl:template match="dml:attribute">
    <xsl:element name="column">
      <!-- Name of the column -->
      <xsl:call-template name="name-attribute">
        <xsl:with-param name="element" select="." />
      </xsl:call-template>
      <!-- Type of the column -->
      <xsl:call-template name="sql-type-attributes">
        <xsl:with-param name="type" select="dml:type" />
        <xsl:with-param name="size" select="dml:type/@size" />
      </xsl:call-template>
      <!-- Other characteristics (primary key, required) -->
      <xsl:choose>
        <xsl:when test="../dml:key/@attributes=@name">
          <xsl:attribute name="primaryKey">true</xsl:attribute>
          <xsl:attribute name="required">true</xsl:attribute>
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="@required='true'">
            <xsl:attribute name="required">true</xsl:attribute>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:element>
  </xsl:template>

  <!-- Creates a table description for the selected domain model description -->
  <xsl:template match="dml:domain">
    <domain name="{@name}">
      <table>
        <xsl:variable name="name" select="@name" />
  
        <!-- Name of the table -->
        <xsl:call-template name="name-attribute">
          <xsl:with-param name="element" select="." />
        </xsl:call-template>
        <!-- Primary key columns first... -->
        <xsl:apply-templates select="dml:attribute[../dml:key/@attributes=@name]" />
        <!-- ...then foreign key columns... -->
        <xsl:call-template name="foreign-item">
          <xsl:with-param name="name" select="$name" />
          <xsl:with-param name="mode">foreign-column</xsl:with-param>
        </xsl:call-template>
        <!-- ...then other columns. -->
        <xsl:apply-templates select="dml:attribute[../dml:key/@attributes!=@name]" />
  
        <!-- Describes the foreign keys for the table -->
        <xsl:call-template name="foreign-item">
          <xsl:with-param name="name" select="$name" />
          <xsl:with-param name="mode">foreign-key</xsl:with-param>
        </xsl:call-template>
      </table>
    </domain>
  </xsl:template>
  
  <!-- Entry point for the transformation -->
  <xsl:template  match="/">
    <database name="unused">
      <!-- Domain model tables description generation -->
      <xsl:apply-templates  select="/dml:dml/dml:domain" />
    
      <!-- Association tables description generation -->
      <xsl:for-each select="/dml:dml/dml:assoc">
        <xsl:variable name="mk" select="count(dml:end[@multiplicity!='1' and @multiplicity!='0..1'])" />
        <xsl:if test="$mk=2">
          <xsl:apply-templates select="." mode="foreign-table" />
        </xsl:if>
      </xsl:for-each>

      <!-- Enumeration tables description generation -->
      <xsl:apply-templates  select="/dml:dml/dml:enum" mode="foreign-table" />
    </database>
  </xsl:template>
  
</xsl:stylesheet>
